﻿/**********************************************************************************************************************/
/* See License.txt file for details.                                                                                  */
/**********************************************************************************************************************/

// This code uses a quick & dirty way of implementing a system tray app
// TODO: Implement the proper system tray support: http://stackoverflow.com/questions/995195/writing-a-windows-system-tray-application-with-net

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using CodePlex.SqlServer.LocalDB.API;

namespace CodePlex.SqlServer.LocalDB.LocalDBCompanion
{
    public partial class LocalDBCompanionMain : Form
    {
        const string AutomaticInstanceName = @"v11.0";
        const string GlobalInstanceName = @"AspNet4PoolId";
        const string AppPoolLogin = @"IIS APPPOOL\ASP.NET v4.0";
        const string ClientConnectionTemplate = @"Server=(localdb)\{0};Integrated Security=SSPI;Database=???";
        const string ErrorCaption = "LocalDB Companion Error";

        private bool reallyClose = false;

        private bool keepRunning = false;
        private DateTime lastPing = DateTime.Now;

        const int PingInterval = 60;

        private bool showingInitialSplashScreen = false;
        private DateTime splashScreenStart;
        
        // Well, the logic lives in the main timer, which fires every 10 seconds
        // So whatever value we put here will likely be extended to 10 seconds.
        const int SplashScreenTime = 5;

        public LocalDBCompanionMain()
        {
            InitializeComponent();
        }

        private bool IsSharedWithAspNetPool(LocalDBInstanceInfo instanceInfo)
        {
            return instanceInfo.IsShared && instanceInfo.SharedName == GlobalInstanceName;
        }

        private void RefreshIcon(LocalDBInstanceInfo instanceInfo)
        {
            if (instanceInfo.IsRunning)
            {
                TaskBarIcon.Icon = Properties.Resources.LocalDBRunning;
            }
            else
            {
                TaskBarIcon.Icon = Properties.Resources.LocalDBStopped;
            }
        }

        private void PingInstance()
        {
            InstanceSharingHelper.PingInstance(AutomaticInstanceName);
            lastPing = DateTime.Now;
        }

        private void PingInstanceIfNeeded()
        {
            if (keepRunning &&
                ((DateTime.Now - lastPing).TotalSeconds > PingInterval))
            {
                PingInstance();
            }
        }

        private void InitSplashScreenTimers()
        {
            showingInitialSplashScreen = true;
            splashScreenStart = DateTime.Now;
        }

        private void CheckSplashScreen()
        {
            if (showingInitialSplashScreen &&
                (DateTime.Now - splashScreenStart).TotalSeconds > SplashScreenTime)
            {
                showingInitialSplashScreen = false;
                Hide();
            }
        }

        private void ShowMessageBaloon(string message)
        {
            TaskBarIcon.BalloonTipText = message;
            TaskBarIcon.ShowBalloonTip(5000);
        }

        private void ShowErrorBaloon(string errorMessage)
        {
            TaskBarIcon.Icon = Properties.Resources.LocalDBBroken;
            ShowMessageBaloon(errorMessage);
        }

        private void LocalDBCompanionMain_Load(object sender, EventArgs e)
        {
            try
            {
                InitSplashScreenTimers();

                var instanceInfo = LocalDBAPI.GetInstanceInfo(AutomaticInstanceName);
                keepRunning = instanceInfo.IsRunning;

                RefreshIcon(instanceInfo);
            }
            catch (Exception error)
            {
                ShowErrorBaloon(error.Message);
            }
        }

        private void LocalDBCompanionMain_Shown(object sender, EventArgs e)
        {
            //if (WindowState == FormWindowState.Minimized)
            //{
            //    Hide();
            //}
        }

        private void LocalDBCompanionMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!reallyClose)
            {
                e.Cancel = true;
                Hide();
            }
        }

        private void MenuItemAbout_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Normal;
            Show();
        }

        private void TaskBarIcon_DoubleClick(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Normal;
            Show();
        }

        private void MenuItemExit_Click(object sender, EventArgs e)
        {
            try
            {
                reallyClose = true;
                Close();
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void txtAbout_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start(e.LinkText);
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void RefreshStateTimer_Tick(object sender, EventArgs e)
        {
            try
            {
                CheckSplashScreen();
                RefreshIcon(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName));
                PingInstanceIfNeeded();
            }
            catch (Exception error)
            {
                ShowErrorBaloon(error.Message);
            }
        }

        private void TaskBarContextMenu_Opening(object sender, CancelEventArgs e)
        {
            try
            {
                var instanceInfo = LocalDBAPI.GetInstanceInfo(AutomaticInstanceName);

                if (instanceInfo.IsRunning)
                {
                    MenuItemStart.Visible = false;
                    MenuItemStop.Visible = true;
                }
                else
                {
                    MenuItemStart.Visible = true;
                    MenuItemStop.Visible = false;
                }

                if (IsSharedWithAspNetPool(instanceInfo))
                {
                    MenuItemShare.Visible = false;
                    MenuItemUnshare.Visible = true;
                }
                else
                {
                    MenuItemShare.Visible = true;
                    MenuItemUnshare.Visible = false;
                }
            }
            catch (Exception error)
            {
                // In case of an error simply disable all menus, except Exit
                MenuItemCopyAddress.Visible = false;
                MenuItemCopyCS.Visible = false;
                MenuSeparator1.Visible = false;
                MenuItemStart.Visible = false;
                MenuItemStop.Visible = false;
                MenuSeparator2.Visible = false;
                MenuItemShare.Visible = false;
                MenuItemUnshare.Visible = false;

                ShowErrorBaloon(error.Message);
            }
        }

        private void MenuItemCopyAddress_Click(object sender, EventArgs e)
        {
            try
            {
                var instanceInfo = LocalDBAPI.GetInstanceInfo(AutomaticInstanceName);
                string serverAddress;
                if (IsSharedWithAspNetPool(instanceInfo))
                {
                    serverAddress = string.Format("(localdb)\\.\\" + GlobalInstanceName);
                }
                else
                {
                    serverAddress = string.Format("(localdb)\\" + AutomaticInstanceName);
                }

                Clipboard.SetText(serverAddress);
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void MenuItemCopyCS_Click(object sender, EventArgs e)
        {
            try
            {
                var instanceInfo = LocalDBAPI.GetInstanceInfo(AutomaticInstanceName);
                string connectionString;
                if (IsSharedWithAspNetPool(instanceInfo))
                {
                    connectionString = string.Format(ClientConnectionTemplate, ".\\" + GlobalInstanceName);
                }
                else
                {
                    connectionString = string.Format(ClientConnectionTemplate, AutomaticInstanceName);
                }

                Clipboard.SetText(connectionString);
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void MenuItemStart_Click(object sender, EventArgs e)
        {
            try
            {
                LocalDBAPI.StartInstance(AutomaticInstanceName);
                keepRunning = true;

                ShowMessageBaloon("Automatic LocalDB Instance is running now.");

                RefreshIcon(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName));
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void MenuItemStop_Click(object sender, EventArgs e)
        {
            try
            {
                keepRunning = false;
                LocalDBAPI.StopInstance(AutomaticInstanceName, 2000);

                ShowMessageBaloon("Automatic LocalDB Instance is stopped.");

                RefreshIcon(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName));
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void MenuItemShare_Click(object sender, EventArgs e)
        {
            try
            {
                InstanceSharingHelper.ShareInstanceWithElevation(AutomaticInstanceName, GlobalInstanceName);

                // For reasons explained inline InstanceSharingHelper.ShareInstanceWithElevation() 
                // doesn't throw on sharing errors. As a workaround I will just check if the instance 
                // is properly shared now.
                if (!IsSharedWithAspNetPool(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName)))
                {
                    throw new Exception("Something went wrong with the instance sharing process. You're on your own :-(");
                }

                InstanceSharingHelper.CreateAdminLogin(AutomaticInstanceName, AppPoolLogin);

                ShowMessageBaloon(string.Format(@"Automatic LocalDB Instance is now shared as (localdb)\.\{0}.", GlobalInstanceName));

                RefreshIcon(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName));
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }

        private void MenuItemUnshare_Click(object sender, EventArgs e)
        {
            try
            {
                InstanceSharingHelper.UnshareInstanceWithElevation(GlobalInstanceName);

                // For reasons explained inline InstanceSharingHelper.UnshareInstanceWithElevation() 
                // doesn't throw on sharing errors. As a workaround I will just check if the instance 
                // is properly shared now.
                if (IsSharedWithAspNetPool(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName)))
                {
                    throw new Exception("Something went wrong with the instance un-sharing process. You're on your own :-(");
                }

                InstanceSharingHelper.DeleteAdminLogin(AutomaticInstanceName, AppPoolLogin);

                ShowMessageBaloon("Automatic LocalDB Instance is not shared anymore.");

                RefreshIcon(LocalDBAPI.GetInstanceInfo(AutomaticInstanceName));
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, ErrorCaption);
            }
        }
    }
}
